package demo.spring.framework.beans.factory.annotation;

import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import demo.vo.UserVO;

/**
 * 使用AnnotationConfigApplicationContext可以实现基于Java的配置类加载Spring的应用上下文。避免使用application.xml进行配置。相比XML配置，更加便捷。
 * <p>
 * 主要涉及到的类或接口有以下几个：
 * 
 * <li>GenericApplicationContext——通用应用上下文，内部持有一个DefaultListableBeanFactory实例，这个类实现了BeanDefinitionRegistry接口，可以在它身上使用任意的bean
 * definition读取器。典型的使用案例是：通过BeanFactoryRegistry接口注册bean
 * definitions，然后调用refresh()方法来初始化那些带有应用上下文语义（org.springframework.context.ApplicationContextAware）的bean，自动探测org.springframework.beans.factory.config.BeanFactoryPostProcessor等。关于这两个接口，在介绍bean的生命周期时进行详细讲解。
 * <li>BeanDefinitionRegistry——用于持有像RootBeanDefinition和 ChildBeanDefinition实例的bean
 * definitions的注册表接口。DefaultListableBeanFactory实现了这个接口，因此可以通过相应的方法向beanFactory里面注册bean。GenericApplicationContext内置一个DefaultListableBeanFactory实例，它对这个接口的实现实际上是通过调用这个实例的相应方法实现的。
 * <li>AbstractApplicationContext——ApplicationContext接口的抽象实现，没有强制规定配置的存储类型，仅仅实现了通用的上下文功能。这个实现用到了模板方法设计模式，需要具体的子类来实现其抽象方法。自动通过registerBeanPostProcessors()方法注册BeanFactoryPostProcessor,
 * BeanPostProcessor和ApplicationListener的实例用来探测bean factory里的特殊bean——对比1分析
 * <li>AnnotationConfigRegistry——注解配置注册表。用于注解配置应用上下文的通用接口，拥有一个注册配置类和扫描配置类的方法。
 *
 */
public class AnnotationConfigApplicationContextDemo {

    @Test
    public void test() {
        AnnotationConfigApplicationContext ctx = build();
        ctx.register(ConfigurationDemo.class);
        ctx.refresh();

        UserVO ent = (UserVO) ctx.getBean("Stan");
        System.out.println(ent.getName());

        UserVO ent2 = (UserVO) ctx.getBean("Nini");
        System.out.println(ent2.getName());

        ctx.close();
        
    }

    /**
     * 无参数构造器，需要通过调用register()方法填充注解类，并进行手动刷新。在这个构造器里初始化了一个读取器和扫描器。
     * 
     * 由于AnnotationConfigApplicationContext是GenericApplicationContext的子类，在调用它的构造器之前会先调用父类的构造器，
     * 父类构造器会实例化一个DefaultListableBeanFactory实例，这个就是基于注解配置的应用上下文的IoC容器。SpringBoot默认调用第一个无参数构造器。
     */
    public AnnotationConfigApplicationContext build() {
        return new AnnotationConfigApplicationContext();
    }

    /**
     * 指定beanFactory
     */
    public AnnotationConfigApplicationContext build(DefaultListableBeanFactory beanFactory) {
        return new AnnotationConfigApplicationContext(beanFactory);
    }

    /**
     * 指定注解类
     */
    public AnnotationConfigApplicationContext build(Class<?>... annotatedClasses) {
        return new AnnotationConfigApplicationContext(annotatedClasses);
    }

    /**
     * 指定包名进行自动扫描并刷新
     */
    public AnnotationConfigApplicationContext build(String... basePackages) {
        return new AnnotationConfigApplicationContext(basePackages);
    }
}
